<?php if(! defined('SYSPATH')) exit('No direct script access allowed');
/*
 +----------------------------------------------------------------------+
 | QuickPHP Framework Version 0.10                                      |
 +----------------------------------------------------------------------+
 | Copyright (c) 2010 QuickPHP.net All rights reserved.                 |
 +----------------------------------------------------------------------+
 | Licensed under the Apache License, Version 2.0 (the 'License');      |
 | you may not use this file except in compliance with the License.     |
 | You may obtain a copy of the License at                              |
 | http://www.apache.org/licenses/LICENSE-2.0                           |
 | Unless required by applicable law or agreed to in writing, software  |
 | distributed under the License is distributed on an 'AS IS' BASIS,    |
 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or      |
 | implied. See the License for the specific language governing         |
 | permissions and limitations under the License.                       |
 +----------------------------------------------------------------------+
 | Author: BoPo <ibopo@126.com>                                         |
 +----------------------------------------------------------------------+
*/
/**
 * @todo [Object Relational Mapping][ref-orm] (ORM) is a method of abstracting database
 *
 * access to standard PHP calls. All table rows are represented as model objects,
 * with object properties representing row data. ORM in QuickPHP generally follows
 * the [Active Record][ref-act] pattern.
 *
 * 对象关系映射 (ORM) 的数据库是一个抽象的方法获得标准的PHP调用。所有表行表示为模型对象，对于对象属性代表行数据。
 *
 * [ref-orm]: http://wikipedia.org/wiki/Object-relational_mapping
 * [ref-act]: http://wikipedia.org/wiki/Active_record
 *
 * $Id: ORM.php 7240 2011-01-22 14:27:59Z bopo $
 *
 * @category    QuickPHP
 * @package     ORM
 * @author      BoPo <ibopo@126.com>
 * @copyright   Copyright &copy; 2010 QuickPHP
 * @license     http://www.quickphp.net/license/
 * @version     $Id: ORM.php 7240 2011-01-22 14:27:59Z bopo $
 */
class QuickPHP_ORM
{

    // 数据对象关系
    protected $_has_one = array();

    protected $_belongs_to = array();

    protected $_has_many = array();

    // Relationships that should always be joined
    // 载入的对象关系
    protected $_load_with = array();

    // 验证成员
    protected $_validate = NULL;

    protected $_rules = array();

    protected $_callbacks = array();

    protected $_filters = array();

    protected $_labels = array();

    // 当前对象
    protected $_object = array();

    protected $_changed = array();

    protected $_related = array();

    protected $_loaded = FALSE;

    protected $_saved = FALSE;

    protected $_sorting;

    // 外键后缀
    protected $_foreign_key_suffix = '_id';

    // 模型表信息
    protected $_object_name;

    protected $_object_plural;

    protected $_table_name;

    protected $_table_columns;

    protected $_ignored_columns = array();

    // Auto-update columns for creation and updates
    // 自动更新柱创造和更新
    protected $_updated_column = NULL;

    protected $_created_column = NULL;

    // Table primary key and value
    // 表的主键和值
    protected $_primary_key = 'id';

    protected $_primary_val = 'name';

    // Model configuration
    // 模型配置
    protected $_table_names_plural = TRUE;

    // Database _reload_on_wakeup
    protected $_reload_on_wakeup = TRUE;

    // Database configuration
    // 数据库配置
    protected $_db = NULL;

    // Database applied data
    // 数据库应用数据
    protected $_db_applied = array();

    // Database pending data
    // 数据库中备案悬
    protected $_db_pending = array();

    // Database reset status
    // 数据库中备案重置地位
    protected $_db_reset = TRUE;

    // Database builder
    // 资料库设计
    protected $_db_builder;

    // With calls already applied
    // 已经用电话
    protected $_with_applied = array();

    // Data to be loaded into the model from a database call cast
    // 数据模型装入电话从数据库中施法
    protected $_preload_data = array();

    // Stores column information for ORM models
    // 商店柱信息,为ORM模型
    protected static $_column_cache = array();

    // Callable database methods
    // 可赎回的数据库中备案方法
    protected static $_db_methods = array(
        'where',
        'and_where',
        'or_where',
        'where_open',
        'and_where_open',
        'or_where_open',
        'where_close',
        'and_where_close',
        'or_where_close',
        'distinct',
        'select',
        'from',
        'join',
        'on',
        'group_by',
        'having',
        'and_having',
        'or_having',
        'having_open',
        'and_having_open',
        'or_having_open',
        'having_close',
        'and_having_close',
        'or_having_close',
        'order_by',
        'limit',
        'offset',
        'cached');

    // Members that have access methods
    // 成员已访问方法
    protected static $_properties = array(
        'object_name',
        'object_plural',
        'loaded',
        'saved', // Object
        'primary_key',
        'primary_val',
        'table_name',
        'table_columns', // Table
        'has_one', 'belongs_to',
        'has_many',
        'has_many_through',
        'load_with', // Relationships
        'validate',
        'rules',
        'callbacks',
        'filters',
        'labels'); // Validation

    /**
     * 创造和返回一个新的模式。
     *
     * @chainable
     * @param   string  model name
     * @param   mixed   parameter for find()
     * @return  ORM
     */
    public static function factory($model, $id = NULL)
    {
        $model = explode("_", $model);

        foreach ($model as $key => $val)
            $model[$key] = ucfirst($val);

        $model = implode("_", $model) . '_Model';
        return new $model($id);
    }

    /**
     * 准备模型数据库连接和载入对象。
     *
     * @param   mixed  parameter for find or object to load
     * @return  void
     */
    public function __construct($id = NULL)
    {
        // Set the object name and plural name
        $this->_object_name   = strtolower(substr(get_class($this), 0, - 6));
        $this->_object_plural = inflector::plural($this->_object_name);

        // Default sorting 缺省排序
        if(! isset($this->_sorting))
            $this->_sorting = array($this->_primary_key => 'ASC');

        // Optimize for performance 优化性能
        if(! empty($this->_ignored_columns))
            $this->_ignored_columns = array_combine($this->_ignored_columns, $this->_ignored_columns);

        /* Initialize database 初始化数据库中备案 */
        $this->_initialize();

        /* Clear the object 清理对象 */
        $this->clear();

        if($id !== NULL)
        {
            if(is_array($id))
            {
                foreach ($id as $column => $value)
                {
                    // Passing an array of column => values 通过一系列的柱= >价值
                    $this->where($column, '=', $value);
                }

                $this->find();
            }
            else
            {
                // Passing the primary key 通过主键
                // Set the object's primary key, but don't load it until needed 设置对象的关键,但不要负荷到所需要的
                $this->_object[$this->_primary_key] = $id;
                // Object is considered saved until something is set 对象被看作救了,直到一些被设置
                $this->_saved = TRUE;
            }
        }
        elseif(! empty($this->_preload_data))
        {
            // Load preloaded data from a database call cast 负荷预数据库的数据叫施放
            $this->_load_values($this->_preload_data);
            $this->_preload_data = array();
        }
    }

    /**
     * Checks if object data is set.
     * 检查是否对象数据被设置。
     *
     * @param   string  column name
     * @return  boolean
     */
    public function __isset($column)
    {
        $this->_load();
        return (isset($this->_object[$column]) or isset($this->_related[$column]) or isset($this->_has_one[$column]) or isset($this->_belongs_to[$column]) or isset($this->_has_many[$column]));
    }

    /**
     * 销毁对象字段。
     *
     * @param   string  列名
     * @return  void
     */
    public function __unset($column)
    {
        $this->_load();
        unset($this->_object[$column], $this->_changed[$column], $this->_related[$column]);
    }

    /**
     * Displays the primary key of a model when it is converted to a string.
     * 显示了主键时模型转化为一个字符串。
     *
     * @return  string
     */
    public function __toString()
    {
        return (string) $this->pk();
    }

    /**
     * Allows serialization of only the object data and state, to prevent
     * "stale" objects being unserialized, which also requires less memory.
     * 允许系列化的只有对象数据,我们的州,防止“场所unserialized物,也需要更少的内存。
     *
     * @return  array
     */
    public function __sleep()
    {
        // Store only information about the object
        return array('_object_name', '_object', '_changed', '_loaded', '_saved', '_sorting');
    }

    /**
     * Prepares the database connection and reloads the object.
     * 准备数据库连接和重新载入的对象。
     *
     * @return  void
     */
    public function __wakeup()
    {
        // Initialize database
        $this->_initialize();

        if($this->_reload_on_wakeup === TRUE)
        {
            // Reload the object
            $this->reload();
        }
    }

    /**
     * Handles pass-through to database methods. Calls to query methods
     * (query, get, insert, update) are not allowed. Query builder methods
     * are chainable.
     * 数据库处理机制的方法。 要求查询方法(查询、获取数据,插入,更新)是不被允许的。 查询生成器方法是链回归。
     *
     * @param   string  method name
     * @param   array   method arguments
     * @return  mixed
     */
    public function __call($method, array $args)
    {
        if(in_array($method, ORM::$_properties))
        {
            if($method === 'loaded')
            {
                if(! isset($this->_object_name))
                {
                    // Calling loaded method prior to the object being fully initialized
                    return FALSE;
                }

                $this->_load();
            }
            elseif($method === 'validate')
            {
                if(! isset($this->_validate))
                {
                    // Initialize the validation object
                    $this->_validate();
                }
            }

            // Return the property
            return $this->{'_' . $method};
        }
        elseif(in_array($method, ORM::$_db_methods))
        {
            // Add pending database call which is executed after query type is determined
            $this->_db_pending[] = array('name' => $method, 'args' => $args);
            return $this;
        }
        else
        {
            throw new QuickPHP_Exception('Invalid method {0} called in {1}', array($method, get_class($this)));
        }
    }

    /**
     * Handles retrieval of all model values, relationships, and metadata.
     * 处理所有的检索模型值、关系和元数据。
     *
     * @param   string  column name
     * @return  mixed
     */
    public function __get($column)
    {
        if(array_key_exists($column, $this->_object))
        {
            $this->_load();
            return $this->_object[$column];
        }
        elseif(isset($this->_related[$column]) and $this->_related[$column]->_loaded)
        {
            // Return related model that has already been loaded
            // 还相关的模式已被载入
            return $this->_related[$column];
        }
        elseif(isset($this->_belongs_to[$column]))
        {
            $this->_load();
            $model = $this->_related($column);
            // Use this model's column and foreign model's primary key
            // 使用这个模型的柱和外国模型的关键
            $col = $model->_table_name . '.' . $model->_primary_key;
            $val = $this->_object[$this->_belongs_to[$column]['foreign_key']];
            $model->where($col, '=', $val)->find();
            return $this->_related[$column] = $model;
        }
        elseif(isset($this->_has_one[$column]))
        {
            $model = $this->_related($column);
            // Use this model's primary key value and foreign model's column
            // 使用这个模型的主键值和外国模型的专栏
            $col = $model->_table_name . '.' . $this->_has_one[$column]['foreign_key'];
            $val = $this->pk();
            $model->where($col, '=', $val)->find();
            return $this->_related[$column] = $model;
        }
        elseif(isset($this->_has_many[$column]))
        {
            $model = ORM::factory($this->_has_many[$column]['model']);

            if(isset($this->_has_many[$column]['through']))
            {
                // Grab has_many "through" relationship table
                // “通过”的关系has_many抓住桌子
                $through = $this->_has_many[$column]['through'];

                // Join on through model's target foreign key (far_key) and target model's primary key
                // 通过模型的目标在一起外键(far_key)和目标模式的主要关键
                $join_col1 = $through . '.' . $this->_has_many[$column]['far_key'];
                $join_col2 = $model->_table_name . '.' . $model->_primary_key;

                $model->join($through)->on($join_col1, '=', $join_col2);

                // Through table's source foreign key (foreign_key) should be this model's primary key
                // 通过表源外键(foreign_key)应该该模型的主要关键
                $col = $through . '.' . $this->_has_many[$column]['foreign_key'];
                $val = $this->pk();
            }
            else
            {
                // Simple has_many relationship, search where target model's foreign key is this model's primary key
                // 简单的has_many关系,在哪里寻觅的目标模式外键被该模型的主要关键
                $col = $model->_table_name . '.' . $this->_has_many[$column]['foreign_key'];
                $val = $this->pk();
            }

            return $model->where($col, '=', $val);
        }
        else
        {
            throw new QuickPHP_Exception('The :property property does not exist in the :class class', array(':property' => $column, ':class' => get_class($this)));
        }
    }

    /**
     * Handles setting of all model values, and tracks changes between values.
     * 处理所有的模特都设置的价值观,和轨道变化之间的价值。
     *
     * @param   string  column name
     * @param   mixed   column value
     * @return  void
     */
    public function __set($column, $value)
    {
        if(! isset($this->_object_name))
        {
            // Object not yet constructed, so we're loading data from a database call cast
            // 对象没有构造,所以我们加载数据库的数据叫施放
            $this->_preload_data[$column] = $value;
            return;
        }

        if(array_key_exists($column, $this->_ignored_columns))
        {
            // No processing for ignored columns, just store it
            // 没有处理,就把它忽略柱
            $this->_object[$column] = $value;
        }
        elseif(array_key_exists($column, $this->_object))
        {
            $this->_object[$column] = $value;
            if(isset($this->_table_columns[$column]))
            {
                // Data has changed
                $this->_changed[$column] = $column;
                // Object is no longer saved
                $this->_saved = FALSE;
            }
        }
        elseif(isset($this->_belongs_to[$column]))
        {
            // Update related object itself
            $this->_related[$column] = $value;
            // Update the foreign key of this model
            $this->_object[$this->_belongs_to[$column]['foreign_key']] = $value->pk();
            $this->_changed[$column] = $this->_belongs_to[$column]['foreign_key'];
        }
        else
        {
            throw new QuickPHP_Exception('The :property: property does not exist in the :class: class', array(':property:' => $column, ':class:' => get_class($this)));
        }
    }

    /**
     * Set values from an array with support for one-one relationships.  This method should be used
     * for loading in post data, etc.
     * 定值从数组支持一对一的关系。 这种方法应该用于加载post数据等。
     *
     * @param   array  array of key => val
     * @return  ORM
     */
    public function values($values)
    {
        foreach ($values as $key => $value)
        {
            if(array_key_exists($key, $this->_object) or array_key_exists($key, $this->_ignored_columns))
            {
                // Property of this model
                // 该模型的财产
                $this->__set($key, $value);
            }
            elseif(isset($this->_belongs_to[$key]) or isset($this->_has_one[$key]))
            {
                // Value is an array of properties for the related model
                // 价值是一个数组的相关性能模型
                $this->_related[$key] = $value;
            }
        }

        return $this;
    }

    /**
     * Prepares the model database connection, determines the table name,
     * and loads column information.
     * 准备模型,确定了数据库连接的表名,和外载荷柱的
     *
     * @return  void
     */
    protected function _initialize()
    {
        // Get database instance
        // 得到数据库实例
        if(! is_object($this->_db))
            $this->_db = Database::instance($this->_db);

        if(empty($this->_table_name))
        {
            // Table name is the same as the object name
            // 表名对象是相同的名字
            $this->_table_name = $this->_object_name;

            if($this->_table_names_plural === TRUE)
            {
                // Make the table name plural
                // 让表名复数形式
                $this->_table_name = inflector::plural($this->_table_name);
            }
        }

        foreach ($this->_belongs_to as $alias => $details)
        {
            $defaults['model'] = $alias;
            $defaults['foreign_key'] = $alias . $this->_foreign_key_suffix;
            $this->_belongs_to[$alias] = array_merge($defaults, $details);
        }

        foreach ($this->_has_one as $alias => $details)
        {
            $defaults['model'] = $alias;
            $defaults['foreign_key'] = $this->_object_name . $this->_foreign_key_suffix;
            $this->_has_one[$alias] = array_merge($defaults, $details);
        }

        foreach ($this->_has_many as $alias => $details)
        {
            $defaults['model'] = inflector::singular($alias);
            $defaults['foreign_key'] = $this->_object_name . $this->_foreign_key_suffix;
            $defaults['through'] = NULL;
            $defaults['far_key'] = inflector::singular($alias) . $this->_foreign_key_suffix;
            $this->_has_many[$alias] = array_merge($defaults, $details);
        }

        // Load column information
        // 荷载列信息
        $this->reload_columns();
    }

    /**
     * Initializes validation rules, callbacks, filters, and labels
     * 初始化属性验证规则,回调,过滤器、和标签
     *
     * @return void
     */
    protected function _validate()
    {
        $this->_validate = Validate::factory($this->_object);

        foreach ($this->_rules as $field => $rules)
            $this->_validate->rules($field, $rules);

        foreach ($this->_filters as $field => $filters)
            $this->_validate->filters($field, $filters);

        // Use column names by default for labels
        // 使用默认值列名称标签
        $columns = array_keys($this->_table_columns);

        // Merge user-defined labels
        // 合并用户自定义标签
        $labels = array_merge(array_combine($columns, $columns), $this->_labels);

        foreach ($labels as $field => $label)
        {
            $this->_validate->label($field, $label);
        }

        foreach ($this->_callbacks as $field => $callbacks)
        {
            foreach ($callbacks as $callback)
            {
                if(is_string($callback) and method_exists($this, $callback))
                {
                    // Callback method exists in current ORM model
                    // 回调的方法在当前的ORM模型
                    $this->_validate->callback($field, array($this, $callback));
                }
                else
                {
                    // Try global function
                    // 试着全局函数
                    $this->_validate->callback($field, $callback);
                }
            }
        }
    }

    /**
     * Returns the values of this object as an array, including any related one-one
     * models that have already been loaded using with()
     * 返回值的数组对象作为,包括任何相关一对一的模型,利用已载入()
     *
     * @return  array
     */
    public function as_array()
    {
        $object = array();

        foreach ($this->_object as $key => $val)
        {
            // Call __get for any user processing
            $object[$key] = $this->__get($key);
        }

        foreach ($this->_related as $key => $model)
        {
            // Include any related objects that are already loaded
            $object[$key] = $model->as_array();
        }

        return $object;
    }

    /**
     * Binds another one-to-one object to this model.  One-to-one objects
     * can be nested using 'object1:object2' syntax
     * 把另一个一对一的反对此模型。 一对一的东西都可以嵌套的object2用“object1:语法
     *
     * @param   string  target model to bind to
     * @return  void
     */
    public function with($target_path)
    {
        if(isset($this->_with_applied[$target_path]))
        {
            // Don't join anything already joined
            return $this;
        }

        // Split object parts
        $aliases = explode(':', $target_path);
        $target  = $this;

        foreach ($aliases as $alias)
        {
            // Go down the line of objects to find the given target
            $parent = $target;
            $target = $parent->_related($alias);

            if(! $target)
            {
                // Can't find related object
                return $this;
            }
        }

        // Target alias is at the end
        $target_alias = $alias;

        // Pop-off top alias to get the parent path (user:photo:tag becomes user:photo - the parent table prefix)
        array_pop($aliases);
        $parent_path = implode(':', $aliases);

        if(empty($parent_path))
        {
            // Use this table name itself for the parent path
            $parent_path = $this->_table_name;
        }
        else
        {
            if(! isset($this->_with_applied[$parent_path]))
            {
                // If the parent path hasn't been joined yet, do it first (otherwise LEFT JOINs fail)
                $this->with($parent_path);
            }
        }

        // Add to with_applied to prevent duplicate joins
        $this->_with_applied[$target_path] = TRUE;
        // Use the keys of the empty object to determine the columns

        foreach (array_keys($target->_object) as $column)
        {
            $name = $target_path . '.' . $column;
            $alias = $target_path . ':' . $column;
            // Add the prefix so that load_result can determine the relationship
            $this->select(array($name, $alias));
        }

        if(isset($parent->_belongs_to[$target_alias]))
        {
            // Parent belongs_to target, use target's primary key and parent's foreign key
            $join_col1 = $target_path . '.' . $target->_primary_key;
            $join_col2 = $parent_path . '.' . $parent->_belongs_to[$target_alias]['foreign_key'];
        }
        else
        {
            // Parent has_one target, use parent's primary key as target's foreign key
            $join_col1 = $parent_path . '.' . $parent->_primary_key;
            $join_col2 = $target_path . '.' . $parent->_has_one[$target_alias]['foreign_key'];
        }

        // Join the related object into the result
        $this->join(array($target->_table_name, $target_path), 'LEFT')->on($join_col1, '=', $join_col2);

        return $this;
    }

    /**
     * Initializes the Database Builder to given query type
     * 初始化资料库设计,给出查询类型
     *
     * @param   int  Type of Database query
     * @return  ORM
     */
    protected function _build($type)
    {
        // Construct new builder object based on query type
        switch ($type)
        {
            case Database::SELECT :
                $this->_db_builder = Database::select();
                break;
            case Database::UPDATE :
                $this->_db_builder = Database::update($this->_table_name);
                break;
            case Database::DELETE :
                $this->_db_builder = Database::delete($this->_table_name);
        }

        // Process pending database method calls
        foreach ($this->_db_pending as $method)
        {
            $name = $method['name'];
            $args = $method['args'];
            $this->_db_applied[$name] = $name;
            switch (count($args))
            {
                case 0 :
                    $this->_db_builder->$name();
                    break;
                case 1 :
                    $this->_db_builder->$name($args[0]);
                    break;
                case 2 :
                    $this->_db_builder
                        ->$name($args[0], $args[1]);
                    break;
                case 3 :
                    $this->_db_builder->$name($args[0], $args[1], $args[2]);
                    break;
                case 4 :
                    $this->_db_builder->$name($args[0], $args[1], $args[2], $args[3]);
                    break;
                default :
                    // Here comes the snail...
                    call_user_func_array(array($this->_db_builder, $name), $args);
                    break;
            }
        }

        return $this;
    }

    /**
     * Loads the given model
     * 载入所建模型
     *
     * @return  ORM
     */
    protected function _load()
    {
        // Only load if it hasn't been loaded, and a primary key is specified and hasn't been modified
        if(! $this->_loaded and ! $this->empty_pk() and ! isset($this->_changed[$this->_primary_key]))
            return $this->find($this->pk());
    }

    /**
     * Finds and loads a single database row into the object.
     * 发现并载入一个单一的数据库排目标。
     *
     * @chainable
     * @param   mixed  primary key
     * @return  ORM
     */
    public function find($id = NULL)
    {
        if(! empty($this->_load_with))
        {
            foreach ($this->_load_with as $alias)
            {
                // Bind relationship
                $this->with($alias);
            }
        }

        $this->_build(Database::SELECT);

        if($id !== NULL)
        {
            // Search for a specific column
            $this->_db_builder->where($this->_table_name . '.' . $this->_primary_key, '=', $id);
        }

        return $this->_load_result(FALSE);
    }

    /**
     * Finds multiple database rows and returns an iterator of the rows found.
     * 发现多个数据库行和返回一个迭代器所废弃的发现。
     *
     * @chainable
     * @return  Database_Result
     */
    public function find_all()
    {
        if(! empty($this->_load_with))
        {
            foreach ($this->_load_with as $alias)
            {
                // Bind relationship
                $this->with($alias);
            }
        }

        $this->_build(Database::SELECT);
        return $this->_load_result(TRUE);
    }

    /**
     * Validates the current model's data
     * 验证当前模型数据
     *
     * @return  boolean
     */
    public function check()
    {
        if(! isset($this->_validate))
        {
            // Initialize the validation object
            $this->_validate();
        }
        else
        {
            // Validation object has been created, just exchange the data array
            $this->_validate->exchangeArray($this->_object);
        }

        if($this->_validate->check())
        {
            // Fields may have been modified by filters
            $this->_object = array_merge($this->_object, $this->_validate->getArrayCopy());
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    }

    /**
     * Saves the current object.
     * 保存当前对象方法
     *
     * @chainable
     * @return  ORM
     */
    public function save()
    {
        if(empty($this->_changed))
            return $this;

        $data = array();

        foreach ($this->_changed as $column)
        {
            // Compile changed data
            $data[$column] = $this->_object[$column];
        }

        if(! $this->empty_pk() and ! isset($this->_changed[$this->_primary_key]))
        {
            // Primary key isn't empty and hasn't been changed so do an update
            if(is_array($this->_updated_column))
            {
                // Fill the updated column
                $column = $this->_updated_column['column'];
                $format = $this->_updated_column['format'];
                $data[$column] = $this->_object[$column] = ($format === TRUE) ? time() : date($format);
            }

            $query = Database::update($this->_table_name)
                ->set($data)
                ->where($this->_primary_key, '=', $this->pk())
                ->execute($this->_db);

            // Object has been saved
            $this->_saved = TRUE;
        }
        else
        {
            if(is_array($this->_created_column))
            {
                // Fill the created column
                $column = $this->_created_column['column'];
                $format = $this->_created_column['format'];
                $data[$column] = $this->_object[$column] = ($format === TRUE) ? time() : date($format);
            }

            $result = Database::insert($this->_table_name)
                ->columns(array_keys($data))
                ->values(array_values($data))
                ->execute($this->_db);

            if($result)
            {
                if($this->empty_pk())
                {
                    // Load the insert id as the primary key
                    // $result is array(insert_id, total_rows)
                    $this->_object[$this->_primary_key] = $result[0];
                }
                // Object is now loaded and saved
                $this->_loaded = $this->_saved = TRUE;
            }
        }

        if($this->_saved === TRUE)
        {
            // All changes have been saved
            $this->_changed = array();
        }

        return $this;
    }

    /**
     * Updates all existing records
     * 更新全部相关数据
     *
     * @chainable
     * @return  ORM
     */
    public function save_all()
    {
        $this->_build(Database::UPDATE);

        if(empty($this->_changed))
            return $this;

        $data = array();

        foreach ($this->_changed as $column)
        {
            // Compile changed data omitting ignored columns
            $data[$column] = $this->_object[$column];
        }

        if(is_array($this->_updated_column))
        {
            // Fill the updated column
            $column = $this->_updated_column['column'];
            $format = $this->_updated_column['format'];
            $data[$column] = $this->_object[$column] = ($format === TRUE) ? time() : date($format);
        }

        $this->_db_builder->set($data)->execute($this->_db);

        return $this;
    }

    /**
     * Deletes the current object from the database. This does NOT destroy
     * relationships that have been created with other objects.
     * 删除当前对象的数据。 这并不破坏,已经创造了关系,与其他对象。
     *
     * @chainable
     * @param   mixed  id to delete
     * @return  ORM
     */
    public function delete($id = NULL)
    {
        if($id === NULL) $id = $this->pk();

        if(! empty($id) or $id === '0')
        {
            // Delete the object
            Database::delete($this->_table_name)
                ->where($this->_primary_key, '=', $id)
                ->execute($this->_db);
        }

        return $this;
    }

    /**
     * Delete all objects in the associated table. This does NOT destroy
     * relationships that have been created with other objects.
     * 删除所有的对象在相关的桌子上。 这并不破坏,已经创造了关系,与其他对象。
     *
     * @chainable
     * @return  ORM
     */
    public function delete_all()
    {
        $this->_build(Database::DELETE);
        $this->_db_builder->execute($this->_db);
        return $this->clear();
    }

    /**
     * Unloads the current object and clears the status.
     * 清空状态操作
     *
     * @chainable
     * @return  ORM
     */
    public function clear()
    {
        // Create an array with all the columns set to NULL
        $values = array_combine(array_keys($this->_table_columns), array_fill(0, count($this->_table_columns), NULL));

        // Replace the object and reset the object status
        $this->_object = $this->_changed = $this->_related = array();

        // Replace the current object with an empty one
        $this->_load_values($values);
        $this->reset();
        return $this;
    }

    /**
     * Reloads the current object from the database.
     * 重新载入了当前对象的数据。
     *
     * @chainable
     * @return  ORM
     */
    public function reload()
    {
        $primary_key = $this->pk();

        // Replace the object and reset the object status
        $this->_object = $this->_changed = $this->_related = array();
        return $this->find($primary_key);
    }

    /**
     * Reload column definitions.
     * 重新读取字段定义信息
     *
     * @chainable
     * @param   boolean  force reloading
     * @return  ORM
     */
    public function reload_columns($force = FALSE)
    {
        if($force === TRUE or empty($this->_table_columns))
        {
            if(isset(ORM::$_column_cache[$this->_object_name]))
            {
                // Use cached column information
                $this->_table_columns = ORM::$_column_cache[$this->_object_name];
            }
            else
            {
                // Grab column information from database
                $this->_table_columns = $this->list_columns(TRUE);
                // Load column cache
                ORM::$_column_cache[$this->_object_name] = $this->_table_columns;
            }
        }

        return $this;
    }

    /**
     * Tests if this object has a relationship to a different model.
     * 返回主表相关的附表是否关联
     * 考验,如果这个对象与不同的模式。
     *
     * @param   string   alias of the has_many "through" relationship
     * @param   ORM      related ORM model
     * @return  boolean
     */
    public function has($alias, $model)
    {
        // Return count of matches as boolean
        return (bool) Database::select(array('COUNT("*")', 'records_found'))
            ->from($this->_has_many[$alias]['through'])
            ->where($this->_has_many[$alias]['foreign_key'], '=', $this->pk())
            ->where($this->_has_many[$alias]['far_key'], '=', $model->pk())
            ->execute($this->_db)
            ->get('records_found');
    }

    /**
     * Adds a new relationship to between this model and another.
     * 添加一个新的关系,在这个模型和另一个表。
     *
     * @param   string   alias of the has_many "through" relationship
     * @param   ORM      related ORM model
     * @param   array    additional data to store in "through"/pivot table
     * @return  ORM
     */
    public function add($alias, ORM $model, $data = NULL)
    {
        $columns = array($this->_has_many[$alias]['foreign_key'], $this->_has_many[$alias]['far_key']);
        $values  = array($this->pk(), $model->pk());

        if($data !== NULL)
        {
            $data       = array_merge(array_combine($columns, $values), $data);
            $columns    = array_keys($data);
            $values     = array_values($data);
        }

        Database::insert($this->_has_many[$alias]['through'])
            ->columns($columns)
            ->values($values)
            ->execute($this->_db);

        return $this;
    }

    /**
     * Removes a relationship between this model and another.
     * 删除一个关系模型的中间表。
     * [code]
     *      $user->remove('roles', ORM::factory('role',array('name'=>'login')));
     * [/code]
     * @param   string   alias of the has_many "through" relationship
     * @param   ORM      related ORM model
     * @return  ORM
     */
    public function remove($alias, ORM $model)
    {
        Database::delete($this->_has_many[$alias]['through'])
            ->where($this->_has_many[$alias]['foreign_key'], '=', $this->pk())
            ->where($this->_has_many[$alias]['far_key'], '=', $model->pk())
            ->execute($this->_db);

        return $this;
    }

    /**
     * Count the number of records in the table.
     * 计算表的记录数
     *
     * @return  integer
     */
    public function count_all()
    {
        $selects = array();

        foreach ($this->_db_pending as $key => $method)
        {
            if($method['name'] == 'select')
            {
                // Ignore any selected columns for now
                $selects[] = $method;
                unset($this->_db_pending[$key]);
            }
        }

        $this->_build(Database::SELECT);

        $records = $this->_db_builder
            ->from($this->_table_name)
            ->select(array('COUNT("*")', 'records_found'))
            ->execute($this->_db)
            ->get('records_found');

        // Add back in selected columns
        $this->_db_pending += $selects;
        $this->reset();

        // Return the total number of records in a table
        return $records;
    }

    /**
     * Proxy method to Database list_columns.
     * 返回表中的字段信息
     *
     * @return  array
     */
    public function list_columns()
    {
        // Proxy to database
        return $this->_db->list_columns($this->_table_name);
    }

    /**
     * Proxy method to Database field_data.
     * 清空缓存
     *
     * @chainable
     * @param   string  SQL query to clear
     * @return  ORM
     */
    public function clear_cache($sql = NULL)
    {
        // Proxy to database
        $this->_db->clear_cache($sql);
        ORM::$_column_cache = array();
        return $this;
    }

    /**
     * Returns an ORM model for the given one-one related alias
     *
     * @param   string  alias name
     * @return  ORM
     */
    protected function _related($alias)
    {
        if(isset($this->_related[$alias]))
        {
            return $this->_related[$alias];
        }
        elseif(isset($this->_has_one[$alias]))
        {
            return $this->_related[$alias] = ORM::factory($this->_has_one[$alias]['model']);
        }
        elseif(isset($this->_belongs_to[$alias]))
        {
            return $this->_related[$alias] = ORM::factory($this->_belongs_to[$alias]['model']);
        }
        else
        {
            return FALSE;
        }
    }

    /**
     * Loads an array of values into into the current object.
     *
     * @chainable
     * @param   array  values to load
     * @return  ORM
     */
    protected function _load_values(array $values)
    {
        if(array_key_exists($this->_primary_key, $values))
        {
            // Set the loaded and saved object status based on the primary key
            $this->_loaded = $this->_saved = ($values[$this->_primary_key] !== NULL);
        }

        // Related objects
        $related = array();

        foreach ($values as $column => $value)
        {
            if(strpos($column, ':') === FALSE)
            {
                if(! isset($this->_changed[$column]))
                {
                    $this->_object[$column] = $value;
                }
            }
            else
            {
                list ($prefix, $column) = explode(':', $column, 2);
                $related[$prefix][$column] = $value;
            }
        }

        if(! empty($related))
        {
            foreach ($related as $object => $values)
            {
                // Load the related objects with the values in the result
                $this->_related($object)->_load_values($values);
            }
        }

        return $this;
    }

    /**
     * Loads a database result, either as a new object for this model, or as an iterator for multiple rows.
     *
     * @chainable
     * @param   boolean       return an iterator or load a single row
     * @return  ORM           for single rows
     * @return  ORM_Iterator  for multiple rows
     */
    protected function _load_result($multiple = FALSE)
    {
        $this->_db_builder->from($this->_table_name);

        if($multiple === FALSE)
        {
            // Only fetch 1 record
            $this->_db_builder->limit(1);
        }

        // Select all columns by default
        // $this->_db_builder->select($this->_table_name . '.*');

        if(! isset($this->_db_applied['order_by']) and ! empty($this->_sorting))
        {
            foreach ($this->_sorting as $column => $direction)
            {
                if(strpos($column, '.') === FALSE)
                {
                    $column = $this->_table_name . '.' . $column;
                }

                $this->_db_builder->order_by($column, $direction);
            }
        }

        if($multiple === TRUE)
        {
            // Return database iterator casting to this object type
            $result = $this->_db_builder
                ->as_object(get_class($this))
                ->execute($this->_db);

            $this->reset();
            return $result;
        }
        else
        {
            // Load the result as an associative array
            $result = $this->_db_builder->as_assoc()->execute($this->_db);
            $this->reset();

            if($result->count() === 1)
            {
                // Load object values
                $this->_load_values($result->current());
            }
            else
            {
                // Clear the object, nothing was found
                $this->clear();
            }

            return $this;
        }
    }

    /**
     * 返回主键的值
     *
     * @return  mixed  primary key
     */
    public function pk()
    {
        return $this->_object[$this->_primary_key];
    }

    /**
     * 判断主键是否为空
     *
     * @return  bool
     */
    protected function empty_pk()
    {
        return (empty($this->_object[$this->_primary_key]) and $this->_object[$this->_primary_key] !== '0');
    }

    /**
     * 返回最后请求SQL字符串
     *
     * @return  string
     */
    public function last_query()
    {
        return $this->_db->last_query;
    }

    /**
     * Clears query builder.  Passing FALSE is useful to keep the existing
     * query conditions for another query.
     *
     * @param  bool  Pass FALSE to avoid resetting on the next call
     */
    public function reset($next = TRUE)
    {
        if($next and $this->_db_reset)
        {
            $this->_db_builder = NULL;
            $this->_db_pending = $this->_db_applied = $this->_with_applied = array();
        }

        // Reset on the next call?
        $this->_db_reset = $next;

        return $this;
    }

}